Creating Geometric Objects
In the previous tutorial we learned to create various user
interface objects, such as windows and buttons. Creating geometric
objects is equally easy. For example, in order to create sphere
objects, you first need to include the header file defining
properties for the sphere class. This can be done by calling:
include("real/objects/r3sphere.js");
Note: You can find all built-in geometric objects from the
'scripts/js/real/objects' folder. Plug-in libraries may define
geometric objects too, in which case their header files can be found
from 'scripts/js/plugins' folder.
Once you have loaded the header file, you can create any number
of sphere instances. For example, the following program creates two
sphere objects.
sphere1 = new r3Sphere(0);
sphere1.SetRadius(0.1);
sphere2 = new r3Sphere(0);
sphere2.SetRadius(0.01);
sphere2.SetCenter(new r3Vect(0.1, 0.1, 0));
The above code should be self explanatory.
Inserting into the Current Project
Once you have created geometric objects, you also need to decide
what to do with them. The most common operation is to insert them
into the current project.
To do this, you need to find the JavaScript object managing
geometric objects in the current project. This can be done by
calling:
primLayer = GetJS("CurrentProject.Geometrics");
Note: The GetJS() function corresponds to the Get() function. However,
the difference is that Get() returns you a Realsoft 3D object, whereas
the GetJS() function creates and returns JavaScript wrapper object.
Then you can insert the two spheres into the current project by
calling:
primLayer.LOCKEXCLUSIVE();
primLayer.INSERT(0, 0, sphere1);
primLayer.INSERT(0, 0, sphere2);
primLayer.RELEASE();
When modifying the contents of a layer, you must always lock the
layer by calling either LOCKEXCLUSIVE() or LOCKSHARED() methods. The
reason for this is that Realsoft 3D is multi threaded application
and two or more threads can access the contents of a layer
simultaneously.
If you are going to change the contents of a layer, a mutually
exclusive 'write' lock must be obtained by calling the LOCKEXCLUSIVE()
method. For example, when you insert a new object into a layer,
mutually exclusively lock must be used.
In case you only want to read the contents of a layer, shared read
lock is sufficient. This can be accomplished by calling the LOCKSHARED()
method.
Both type of locks can be released by calling the RELEASE() method.
Creating Levels
To create a level object:
include("real/objects/r3level.js");
level = new r3Level(0);
Let's imagine we want to create couple of sub objects for the
newly created level object, say two cubes.
include("real/objects/r3cube.js");
cube1 = new r3Cube(0);
cube1.SetParent(level);
cube1.SetP0(new r3Vect(0.0, 0.0, 0.0);
cube1.SetP1(new r3Vect(0.2, 0.0, 0.0);
cube1.SetP2(new r3Vect(0.0, 0.2, 0.0);
cube1.SetP3(new r3Vect(0.0, 0.0, 0.2);
cube2 = new r3Cube(0);
cube2.SetParent(level);
cube2.SetP0(new r3Vect(0.1, 0.1, 0.0);
cube2.SetP1(new r3Vect(0.25, 0.0, 0.0);
cube2.SetP2(new r3Vect(0.0, 0.25, 0.0);
cube2.SetP3(new r3Vect(0.0, 0.0, 0.25);
Note that analytic cubes are defined by four points: one corner point
plus three associated 'edge' points. The remaining points for the
cube are defined internally, so you don't have to (actually, can't)
define them.
A level object also defines the 'Boolean' attribute for boolean
operations. Let's turn the level object to a boolean operation, so that
the two sub cubes gets boolean operated with each other.
level.SetBoolean(R3BOOL_AND);
Our hierarchical level object is now ready so we can insert it
into the current project by calling:
primLayer.LOCKEXCLUSIVE();
primLayer.INSERT(0, 0, level);
primLayer.RELEASE();
Transforming Objects
All geometric objects define an attribute called 'Matrix', which
defines so called local object space for the object. For example, an
object can be moved by constructing the desired transformation matrix
and concantenating it to the object's matrix.
The Matrix class defines four methods for setting up transformation
matrices: translate(), scale(), rotate() and skew().
Let's imagine we need to translate the selected objects by 0.1
meters on the world 'x' axis. To construct a transformation matrix
which translates in world x-axis, call:
m = new r3Matrix();
m.translate(0.1, 0, 0);
Then you can apply the transformation matrix to the selected
objects by calling:
primLayer.LOCKEXCLUSIVE();
primLayer.TRANSFORM(m);
primLayer.RELEASE();
You may concatenate any number of matrix operations into a single
matrix. In this case, the last concatenated operation is executed
first. For example, if you want to move and scale the selected
objects, call:
m = new r3Matrix();
m.scale(0.5 0.5, 0.5);
m.translate(0.1, 0, 0);
in which case the selected objects are first translated and then
scaled.
Any transformation effect, such as scaling, rotation or skew, or
any combination of them can be constructed and applied this way.
Single Point Editing
The TRANSFORM() method allows you to apply any linear transformation
to an object. However, it does not allow you to deform the geometry of
an object.
The geometry base class ('scripts/js/real/objects/r3prim.js)
defines the SETPOINT() method, allowing you to set any of the geometric
attributes defined by the object in question.
Let's create a nurbs curve consisting of four control points.
include("real/objects/r3nurbs.js");
nurbs = new r3Nurbs(0);
nurbs.SetOrder(4);
nurbs.SetCount(4);
nurbs.SetKnots([0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0]);
for(i = 0; i < 4; i++)
nurbs.SETPOINT(i, new r3Vect(i * 0.1, 0, 0));
Here we used SETPOINT() method to define initial geometry for the
nurbs curve.
Another useful method is GETPOINT(), which allows you to fetch
the value of any desired geometry point. By using GETPOINT and SETPOINT
methods, you can apply deformation effects to geometric objects.
First you call GETPOINT() to fetch the current value, then you modify
the value using some non linear function and finally you assign the
modified value back to the object by calling SETPOINT().
For example, the following uses fractal noise to deform all
geometric points of the given object.
function applyNoise(obj)
{
// create new vector object
p = new r3Vect();
// fetch the number of geometry points in the given object
pcount = obj.GetPointCount();
for(i = 0; i < pcount; i++) {
// fetch the current value of the point 'i'
obj.GETPOINT(p, i);
// displace the point using fractal noise
p2 = p.noise(3, 4);
p2.fmul(0.1);
p.fadd(p2);
// set displaced point back to the object
obj.SETPOINT(i, p);
}
}
Enumerating Selected Objects
Now that we managed to implement a somewhat interesting deformation
effect, let's see how it could be plugged in as a new deformation
tool.
The layer base class defines a method which is very useful when
implementing custom tools. The method is called ENUMSELECTLIST().
This method scans through the currently selected objects and passes
them to the given hook function, such as the deformation function we
wrote above.
There is only one complication. Because the JavaScript objects
are 'wrapper' objects attached to actual Realsoft 3D objects, our
hook function will be called with a Realsoft 3D object rather than
a JavaScript object. The low level Realsoft 3D functionality does not
know if the enumeration request was generated by JavaScript
wrapper object. Therefore, we must create a JavaScript interface to
the given Realsoft 3D object before we can access its properties through JavaScript methods.
Here is the code:
function deformByNoiseCallBack(r3obj)
{
// create JavaScript interface to 'r3obj'
jsobj = R3ToJS(r3obj);
// call the applyNoise() function
if(obj)
return applyNoise(jsobj);
return 1;
}
Now you can deform the selected objects by calling:
primLayer.LOCKEXCLUSIVE();
primLayer.ENUMSELECTLIST([R3RA_Hook, mycallback]);
primLayer.RELEASE();
The 'R3RA_Hook' parameter is used for specifying the callback
function to be called.
You can find this example from the
'scripts/js/myclasses/toolbars/mydeform.js'. To test it, create
couple of SDS or NURBS objects, select them and apply
'Scripts/Toolbars/mydeform.js' pull-down menu.